import os
import time

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.optimize import linprog

from topsis import readData
from topsis.topsis_config import topsis_config

plt.rcParams['font.sans-serif'] = ['SimHei']  # 步骤一（替换sans-serif字体）
plt.rcParams['axes.unicode_minus'] = False  # 步骤二（解决坐标轴负数的负号显示问题）
WEIGHTFLOW_DOWN = topsis_config.WEIGHTFLOW_DOWN
WEIGHTFLOW_UP = topsis_config.WEIGHTFLOW_UP
SUM_WEIGHT = topsis_config.SUM_WEIGHT

# 显示pandas dataframe的所有行
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)


# 熵权法
def entropyWeight(data):
    data = np.array(data)
    # 归一化
    P = data / data.sum(axis=0)
    # 计算熵值
    E = np.nansum(-P * np.log(P) / np.log(len(data)), axis=0)
    # 计算权系数
    return (1 - E) / (1 - E).sum()


# 读取数据，并将id和产出数据分离
def topsis_read_data(dir, sheetname, dataframe=None):
    if dataframe is None:
        dataset = pd.read_excel(dir, sheet_name=sheetname)
    else:
        dataset = dataframe
    # print(dataset)
    column_list = {}
    try:
        column_list = dataset[topsis_config.FIELDS]
    except Exception as e:
        print("列名出错，读取失败！")
        print(e)
        exit()
    dataset = column_list.fillna(0)
    dataset = dataset.mask(dataset == '被冻结', 00000)
    uid = dataset.iloc[:, np.r_[0]]  # 获取第一列
    outcomes = dataset[topsis_config.FIELDS_OUTCOME]  # 获取后面的列
    return uid, outcomes


def getTimeStr():
    return time.strftime("%Y_%m_%d_%H_%M_%S")


# 用每个分数的排名来代替原始分数
def calculate_rank(data):
    rank_data = data.copy()
    for column in rank_data.columns:
        sorted_column_list = sorted(rank_data[column].values.tolist(), reverse=True)
        sorted_set = set(sorted_column_list)
        rank_dict = {}
        for value in sorted_set:
            rank_dict[value] = sorted_column_list.index(value)
        for idx in rank_data.index.values:
            rank_data[column][idx] = rank_dict[rank_data[column][idx]] + 1
    return rank_data


# 线性规划计算权重
def get_weights(data, weights_list):
    res = []
    res_quan = []
    for row in range(data.shape[0]):
        c = -1 * data.iloc[row]
        A_eq = np.array([[1] * data.shape[1]])
        B_eq = np.array([SUM_WEIGHT])
        bo = []
        for co in range(len(data.columns)):
            up_bo = weights_list[data.columns[co]] + WEIGHTFLOW_UP
            low_bo = weights_list[data.columns[co]] - WEIGHTFLOW_DOWN
            if low_bo < 0:
                low_bo = 0
            bo.append([low_bo, up_bo])
        bo = tuple(bo)
        linear_res = linprog(c, A_eq=A_eq, b_eq=B_eq, bounds=bo)
        res.append(linear_res['x'].tolist())
    return np.array(res)


# topsis
def topsis(uid, data, weight=None):
    data = data.fillna(0)
    # 使用排名代替
    # 拼接 id
    new_data = pd.concat([uid, data], axis=1)
    # 平滑处理
    data = np.log(np.add(data, 1))
    # 归一化
    data = data / np.sqrt((data ** 2).sum())

    # 最优最劣方案
    Z = pd.DataFrame([data.min(), data.max()], index=['负理想解', '正理想解'])
    # 求权重使得离正理想解的距离最小。离正理想解的距离最小就意味着离负理想解的距离最大。
    weight_new = get_weights(((data - Z.loc['正理想解']) ** 2), topsis_config.WEIGHTS)
    weight_new = np.array(weight_new)

    score_Result = data.copy()
    score_Result['正理想解'] = (np.sqrt(((data - Z.loc['正理想解']) ** 2) * weight_new).sum(axis=1))
    score_Result['负理想解'] = (np.sqrt(((data - Z.loc['负理想解']) ** 2) * weight_new).sum(axis=1))

    # 综合得分指数
    score_Result['综合得分指数'] = score_Result['负理想解'] / (score_Result['负理想解'] + score_Result['正理想解'])
    score_Result = score_Result[['综合得分指数']]

    score_Result = np.column_stack((pd.DataFrame(uid), pd.DataFrame(score_Result)))
    score_Result = pd.DataFrame(score_Result)
    score_Result.columns = ['uid', 'score']
    score_Result = score_Result.sort_values(by='score', ascending=False)
    return score_Result, new_data


# topsis_rank
def topsis_rank(id, data, weight=None):
    data = data.fillna(0)

    rank_data = calculate_rank(data)

    datalen = len(data) + 1
    data = (datalen - rank_data) / len(data)
    new_data = pd.concat([id, rank_data], axis=1)
    new_data.to_excel("data_rank.xlsx", index=False)
    # data = 1 / d
    # 平滑数据处理
    # data = np.log(np.add(data, 1))

    data = data / np.sqrt((data ** 2).sum())
    data.to_excel('guiyihua.xlsx', index=False)
    weight = get_weights(data, topsis_config.WEIGHTS)
    weight_zong = pd.concat([new_data, pd.DataFrame(weight)], axis=1)
    weight_zong.to_excel("weight_zong.xlsx", index=False)
    weight = np.array(weight)

    # 最优最劣方案
    Z = pd.DataFrame([data.min(), data.max()], index=['负理想解', '正理想解'])
    Z.to_excel('zuiyouzuilie.xlsx')

    score_Result = data.copy()
    score_Result['正理想解'] = (np.sqrt(((data - Z.loc['正理想解']) ** 2) * weight).sum(axis=1))
    score_Result['负理想解'] = (np.sqrt(((data - Z.loc['负理想解']) ** 2) * weight).sum(axis=1))

    # 综合得分指数
    score_Result['综合得分指数'] = score_Result['负理想解'] / (score_Result['负理想解'] + score_Result['正理想解'])
    score_Result = score_Result[['综合得分指数']]

    score_Result = np.column_stack((pd.DataFrame(id), pd.DataFrame(score_Result)))
    score_Result = pd.DataFrame(score_Result)
    score_Result.columns = ['id', 'score']
    score_Result = score_Result.sort_values(by='score', ascending=False)
    return score_Result, new_data


def get_groundTruth(fname):
    return readData.read_Pinfo(fname)['win']


# 主执行函数
def do_topsis(fname=None, dataframe=None):
    dir = topsis_config.Data_Dir + fname
    sheet_name = 'bids_sheet'
    grand_truth = get_groundTruth(fname)
    project = readData.read_Pinfo(fname)
    pid = project['pid']
    rate = project['rate']
    uid, dataset = topsis_read_data(dir, sheet_name)
    # 执行
    result, rank_data = topsis_rank(uid, dataset)
    maxx = result['score'].max()
    w = 1 / maxx
    # minn = result['score'].min()
    # res = np.sqrt((result['score'] - minn) * w)
    # result['score'] = res
    result['uid'] = result['uid'].astype(int)
    rank_data['uid'] = rank_data['uid'].astype(int)
    result['score'] = result['score'].apply(lambda x: format(x, '.3f')).astype(float)
    z = pd.merge(rank_data, result, on="uid")
    z.loc[z['uid'] == int(grand_truth), 'uid'] = f'{grand_truth}*'
    z = z.sort_values(by='score', ascending=False)
    writer = pd.ExcelWriter(topsis_config.Output_Dir + f'finalscore_{pid}.xls')
    z.to_excel(writer, sheet_name='Sheet1')
    pro_pd = pd.DataFrame(project)
    pro_pd.to_excel(writer, sheet_name='Sheet2')
    writer.save()
    axis1 = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
    axis2 = [0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95,
             1]
    # 拉伸分数
    result['score'] = result["score"] * w
    rank_predict = list(result['uid']).index(int(grand_truth)) + 1
    return pid, rank_predict, grand_truth, result, rate
    # plt.hist(result['score'], bins=axis1, rwidth=0.5)
    # print(position_identify(result))
    # plt.xlabel("分数")
    # plt.ylabel("人数")
    # plt.show()


def get_zc_pinfo(pid):
    zcfname = 'pinfo_' + str(pid) + '.xls'
    return


def pinfo_list():
    # infolist = get_Pinfo_Data_Search()
    # datas = pd.DataFrame(infolist)
    d_2017 = pd.read_excel('d_fin_2017_ex.xls')
    # d_all = pd.read_excel('p_info_sum.xls')
    data_use = d_2017[d_2017['date'] > DATA_YEAR]
    data_use = data_use['pid']
    data_use = ['pinfo_' + str(d) + '.xls' for d in data_use]
    return data_use


def get_imlist():
    return [f for f in os.listdir(topsis_config.Data_Dir) if f.endswith('xls')]


def do_scan_topsis():
    # 获取所有p的列表
    flist = pinfo_list()
    totalnum = len(flist)
    totalnum = 0
    top1rightnum = 0
    top3rightnum = 0
    top5rightnum = 0
    mr = 0
    ratedic = {0: 0, 1: 0, 2: 0, 3: 0}
    for f in flist:
        pid, rank_predict, grand_truth, result, rate = do_topsis(f)
        ratedic[rate] = ratedic[rate] + 1
        if (rate != 3):
            continue
        totalnum = totalnum + 1
        top1right, top3right, top5right = 0, 0, 0
        if (rank_predict == 1):
            top1right, top3right, top5right = 1, 1, 1
        elif rank_predict <= 3:
            top1right, top3right, top5right = 0, 1, 1
        elif rank_predict <= 5:
            top1right, top3right, top5right = 0, 0, 1
        top1rightnum = top1rightnum + top1right
        top3rightnum = top3rightnum + top3right
        top5rightnum = top5rightnum + top5right
        mr = mr + float(1 / rank_predict)
    print(f'totalnum :{totalnum}, top1rightnum:{top1rightnum},right_rate = {format(top1rightnum / totalnum, ".3f")}')
    print(f'totalnum :{totalnum}, top3rightnum:{top3rightnum},right_rate = {format(top3rightnum / totalnum, ".3f")}')
    print(f'totalnum :{totalnum}, top5rightnum:{top5rightnum},right_rate = {format(top5rightnum / totalnum, ".3f")}')
    print(f'totalnum :{totalnum}, mr:{format(mr, ".2f")},mrr = {format(mr / totalnum, ".3f")}')
    print('ratedic :')
    print(ratedic)


if __name__ == '__main__':
    # res = get_rank('test.xlsx', sheet_name="Sheet2")
    # res = get_rank('pinfo_231305.xls')
    # print(res)
    # print(topsis_config.Data_Dir)
    DATA_YEAR = '2017'
    do_scan_topsis()

    #     test one
    # pid, top1right, top3right, top5right, top1, grand_truth, result= get_rank('pinfo_229257.xls')
